﻿' •————————————————————————————————————————————————————————————————————————————————————————————————————————————•
' |                                                                                                            |
' |    GeoLock is a proof of concept (PoC). GeoLock uses Geosense for Windows,                                 |
' |    Microsoft Windows 7 Sensor and Location Interop Sample Library, Microsoft WindowsAPICodePack            |
' |    and Microsoft Bing Maps SDK to demonstrate how to retrieve geolocation data without                     |
' |    Global Positioning System (GPS) hardware.                                                               |
' |                                                                                                            |
' |    The GeoLocK application was written in VB.NET (WPF). The COM Interop Windows7.SensorAndLocation.dll,    |
' |    Microsoft.WindowsAPICodePack.dll and Microsoft.Maps.MapControl.WPF.dll supplied                         |
' |    with this application are x86 binaries.                                                                 |
' |                                                                                                            |
' |    Copyright (C) 2012 Pascal Hubert aka Néo <http://geolock.codeplex.com/>                                 |
' |                                                                                                            |
' |    Project administrator : Pascal Hubert aka Néo (neojudgment@hotmail.com)                                 |
' |                                                                                                            |
' |    This program is under Microsoft Public License (Ms-PL)                                                  |
' |                                                                                                            |
' |    You should have received a copy of the Microsoft Public License (Ms-PL)                                 |
' |    along with this program.  If not, see <http://msdn.microsoft.com/en-us/library/ff647936.aspx>.          |
' |                                                                                                            |
' •————————————————————————————————————————————————————————————————————————————————————————————————————————————•
Imports System.Device.Location
Imports System.IO
Imports System.Windows.Threading
Imports System.ComponentModel
Imports System.Windows.Forms
Imports Microsoft.WindowsAPICodePack.ApplicationServices
Imports System.Net
Imports System.Reflection
Imports System.Net.Security
Imports System.Threading
Imports Windows7.Sensors
Imports Microsoft.Maps.MapControl.WPF
Imports System.IO.Ports
Imports System.Runtime.InteropServices
Imports System.Xml

Public Class MainWindow
    <Runtime.InteropServices.DllImport("wininet", EntryPoint:="InternetGetConnectedState", ExactSpelling:=True, CharSet:=Runtime.InteropServices.CharSet.Ansi, SetLastError:=True)> _
    Private Shared Function InternetGetConnectedState(ByRef dwFlags As Long, ByVal dwReserved As Long) As Long
    End Function

    <Runtime.InteropServices.DllImport("psapi.dll", EntryPoint:="EmptyWorkingSet", ExactSpelling:=True, CharSet:=Runtime.InteropServices.CharSet.Ansi, SetLastError:=True)> _
    Private Shared Function EmptyWorkingSet(ByVal hProcess As IntPtr) As Boolean
    End Function

    Private _mysensors As Sensor
    Private _geosenseSensor As Boolean
    Private _ipAdd As String
    Private _internetConnection As Boolean
    Private _firstStart As Boolean
    Private _batteryState As String
    Private _bingMapLoaded As Boolean
    Private _bearing As Double

    Private ReadOnly _watcher As New GeoCoordinateWatcher(GeoPositionAccuracy.High)
    Private ReadOnly _sw As New StreamWriter(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\GeoLock\GeoLock.log", False)
    Private ReadOnly _tl As New TextWriterTraceListener(_sw)
    Private ReadOnly _httpsReverseGeocoding As String

    Private WithEvents _timerBattery As DispatcherTimer
    Private WithEvents _timerWiFi As DispatcherTimer
    Private WithEvents _timerSensorErr As DispatcherTimer
    Private WithEvents _timerGeolockUpdate As DispatcherTimer

    Private WithEvents _spa As SerialPort
    Private WithEvents _timerGpsScan As DispatcherTimer

    Public Shared FormTop As Double
    Public Shared FormLeft As Double
    Public Shared SensorNotPresent As Boolean
    Public Shared MyWindowMove As Boolean

    Private _latitude As Double
    Private _longitude As Double
    Private _altitude As String
    Private _dilution As String
    Private _altitudedouble As Double
    Private _dilutiondouble As Double

    Public Sub New()

        InitializeComponent()

        ' On enregistre l'application afin que celle-ci redémarre automatiquement si elle c'est terminée
        ' pour une raison autre qu'un redémarrage du système ou une mise à jour du système.
        ApplicationRestartRecoveryManager.RegisterForApplicationRestart(New RestartSettings("/restart", RestartRestrictions.NotOnReboot Or RestartRestrictions.NotOnPatch))

        ' Initialisation du Trace.Listeners
        InitialisationTraceListener()
        Trace.WriteLine(DateTime.Now & " " & "Démarrage de GeoLock...")

        ' On va rechercher l'ancien user.config si le num de version est différent
        Dim version As String = Assembly.GetEntryAssembly().GetName().Version.ToString()
        With My.Settings
            If .CurrentVersion <> version Then
                .Upgrade()
                .CurrentVersion = version
                .Save()
            End If
        End With

        ' Clé Bing Maps
        BingMap.CredentialsProvider = New ApplicationIdCredentialsProvider(My.Settings.BingMapKey)

        ' Utilisation du protocole HTTPS
        If My.Settings.UseHttps Then
            Map.UseHttps = True
            _httpsReverseGeocoding = "https://"
        Else
            _httpsReverseGeocoding = "http://"
        End If

        ' Activer et configurer si derrière un proxy
        If My.Settings.UseProxy Then
            If (Not String.IsNullOrEmpty(My.Settings.ProxyAdress)) AndAlso My.Settings.ProxyPort > 0 Then
                WebRequest.DefaultWebProxy = New WebProxy(My.Settings.ProxyAdress & ":" & My.Settings.ProxyPort)
                WebRequest.DefaultWebProxy.Credentials = New NetworkCredential(My.Settings.ProxyUsername,
                 My.Settings.ProxyPassword)
            End If
        End If

        BingMap.Visibility = Visibility.Hidden

        ' Initialisation des Timer
        _timerBattery = New DispatcherTimer(DispatcherPriority.Background)
        _timerBattery.Interval = TimeSpan.FromSeconds(1)

        _timerWiFi = New DispatcherTimer(DispatcherPriority.Background)
        _timerWiFi.Interval = TimeSpan.FromSeconds(3)

        _timerSensorErr = New DispatcherTimer(DispatcherPriority.Normal)
        _timerSensorErr.Interval = TimeSpan.FromSeconds(3)

        _timerGeolockUpdate = New DispatcherTimer(DispatcherPriority.Background)
        _timerGeolockUpdate.Interval = TimeSpan.FromSeconds(3)

        _timerGpsScan = New DispatcherTimer(DispatcherPriority.Normal)
        _timerGpsScan.Interval = TimeSpan.FromSeconds(2)

        Try

            ' On teste la connection Internet au démarrage
            If ConnexionInternetPresente() Then
                _internetConnection = True
            End If

            _firstStart = True

            ' Etat de l'alimentation au démarage
            If PowerManager.IsBatteryPresent Then
                ' Le niveau de la batterie est bas
                If PowerManager.BatteryLifePercent < 20 Then
                    _timerBattery.Start()
                End If
            Else
                _batteryState = "high"
            End If

            ' On surveille l'état de l'alimentation
            AddHandler PowerManager.BatteryLifePercentChanged, AddressOf PowerModeChanged

        Catch ex As Exception
            MessageBox.Show("Sub New: " & ex.ToString)
            Trace.WriteLine(DateTime.Now & "Sub New: " & ex.ToString)
        End Try
    End Sub

    Private Sub InitialisationTraceListener()

        Trace.Listeners.Clear()
        Trace.Listeners.Add(_tl)
        Trace.AutoFlush = True
        Trace.WriteLine(DateTime.Now & " " & "InitialisationTraceListener")
    End Sub

    Private Sub OnPositionChanged(ByVal sender As Object, ByVal geo As GeoPositionChangedEventArgs(Of GeoCoordinate))

        Trace.WriteLine(DateTime.Now & " " & "OnPositionChanged")

        If Not MyWindowMove Then

            Try

                ' C'est le 1er démarrage. On recherche l'ip
                If _firstStart Then
                    If _internetConnection Then
                        _ipAdd = ExternalIp()
                    End If
                End If

                ' Ce n'est pas le 1er démarrage. On recherche l'ip
                If (Not _firstStart) AndAlso ConnexionInternetPresente() Then
                    If _ipAdd <> "Unavailable" Then
                        _ipAdd = ExternalIp()
                    End If
                    _internetConnection = True
                End If

                ' recherche à partir de la latitude/longitude
                Dim resolver As New CivicAddressResolver()
                If Not geo.Position.Location.IsUnknown Then

                    Dim address As CivicAddress = resolver.ResolveAddress(geo.Position.Location)

                    With geo.Position
                        LabelTimeStamp.Content = "TimeStamp: " & .Timestamp.LocalDateTime
                        LabelLatitudeDegrees.Content = "Latitude Degrees: " & .Location.Latitude
                        LabelLongitudeDegrees.Content = "Longitude Degrees: " & .Location.Longitude
                        LabelAltitudeSeaLevelMeters.Content = "Altitude SeaLevel Meters: " & .Location.Altitude.ToString("0.0").Replace(".", ",")
                        LabelErrorRadiusMeters.Content = "Error Radius Meters: ± " & .Location.HorizontalAccuracy.ToString("0.0").Replace(".", ",")
                        LabelBearingDegrees.Content = "Bearing Degrees: " & .Location.Course
                        LabelSatelliteInView.Content = "Satellites In View: " & "None"
                        LabelSpeedMetersSecond.Content = "Speed Meters Second: " & .Location.Speed.ToString
                    End With

                    With address
                        ' Affichage des données si l'adresse est disponible
                        If Not .IsUnknown Then
                            LabelAdress1.Content = "Adress1: " & .AddressLine1
                            LabelAdress2.Content = "Adress2: " & .AddressLine2
                            LabelCity.Content = "City: " & .City
                            LabelCountryRegion.Content = "Country Region: " & .CountryRegion
                            LabelStateProvince.Content = "State Province: " & .StateProvince
                            LabelPostalCode.Content = "Postal Code: " & .PostalCode
                        End If
                    End With

                    LabelRealTimeStatus.Content = "RealTime Status: Acquisition In Progress..."

                    Try
                        If _internetConnection Then

                            ' Affichage de l'IP
                            If _ipAdd IsNot String.Empty Then
                                LabelCurrentIP.Content = "Current IP: " & _ipAdd
                            Else
                                LabelCurrentIP.Content = "Current IP: Unresolved"
                            End If

                            ' Afficher la maps et PushPin
                            If geo.Position.Location.Latitude > 0.0 AndAlso geo.Position.Location.Longitude > 0.0 Then

                                ' Déclarations du Custom PushPin
                                Dim mapLayer As New MapLayer()
                                Dim customPushPin As New Image()
                                With customPushPin
                                    .Source = New BitmapImage(New Uri("Resources\CustomPushPin.png", UriKind.Relative))
                                    .Width = 32
                                    .Height = 32
                                End With

                                ' Suppression et ajout du MapLayer
                                With BingMap.Children
                                    .Clear()
                                    .Add(mapLayer)
                                End With

                                Dim location As New Location()
                                With location
                                    .Latitude = geo.Position.Location.Latitude
                                    .Longitude = geo.Position.Location.Longitude
                                End With

                                ' Ajout du Custom PushPin sur le MapLayer
                                mapLayer.SetPosition(customPushPin, location)
                                mapLayer.SetPositionOrigin(customPushPin, PositionOrigin.Center)
                                mapLayer.Children.Add(customPushPin)

                                ' Affichage de la maps
                                With BingMap
                                    ' C'est le premier démarrage
                                    If _firstStart OrElse Not _bingMapLoaded Then
                                        .AnimationLevel = AnimationLevel.Full
                                        .UseInertia = True
                                        .SetView(location, My.Settings.ZoomLevel)
                                        Trace.WriteLine(DateTime.Now & " " & "Premier démarrage affichage de la carte")
                                    Else
                                        ' On déplace la carte (pas besoin de request).
                                        .AnimationLevel = AnimationLevel.None
                                        .UseInertia = True
                                        .Center = location
                                        .ZoomLevel = My.Settings.ZoomLevel
                                        Trace.WriteLine(DateTime.Now & " " & "Déplacement de la carte après changement de location (pas de request)")
                                    End If
                                End With

                                ' La Maps est chargée
                                If BingMap IsNot Nothing Then
                                    _bingMapLoaded = True
                                End If

                                If BingMap.Visibility = Visibility.Hidden Then
                                    BingMap.Visibility = Visibility.Visible
                                End If

                            End If
                        Else
                            ' Pas de connection internet
                            _bingMapLoaded = False
                            BingMap.Visibility = Visibility.Hidden
                        End If

                        With My.Settings
                            .LastLatitude = geo.Position.Location.Latitude
                            .LastLongitude = geo.Position.Location.Longitude
                        End With

                        _internetConnection = False
                        _firstStart = False

                    Catch ex As Exception
                        ' La maps n'est pas disponible
                        _bingMapLoaded = False
                        BingMap.Visibility = Visibility.Hidden
                    End Try
                Else
                    ' L'emplacement n'est pas connu
                    LabelRealTimeStatus.Content = "RealTime Status: Location Is Unknown..."
                End If

                ' L'adresse est connue. On arrête les Timer
                If geo.Position.Location.Latitude > 0.0 AndAlso geo.Position.Location.Longitude > 0.0 Then

                    _timerSensorErr.Stop()

                    ' Code équivalent de l'Application.DoEvents()
                    Application.Current.Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Background, New ThreadStart(Sub()

                                                                                                                           End Sub))

                    Trace.WriteLine(DateTime.Now & " " & geo.Position.Location.Latitude & " " & geo.Position.Location.Longitude)

                End If

                ' Nettoyage de la mémoire
                MemoryClean()

            Catch ex As Exception
                BingMap.Visibility = Visibility.Hidden
                Trace.WriteLine(DateTime.Now & " " & "OnPositionChanged Error: " & ex.ToString)
            End Try
        End If

        MyWindowMove = False
    End Sub

    Private Sub TimerSensorErrorTick(sender As Object, e As EventArgs) Handles _timerSensorErr.Tick

        Trace.WriteLine(DateTime.Now & " " & "TimerSensorErrorTick")

        StopAddHandlerWatcher()
        AddHandlerWatcher()
    End Sub

    Private Sub AddHandlerWatcher()

        Trace.WriteLine(DateTime.Now & " " & "AddHandlerWatcher")

        Try
            Dim watcher As New GeoCoordinateWatcher(GeoPositionAccuracy.High)

            ' 25 mètres de déplacement pour mise à jour de PositionChanged, rafraichir les infos et la maps
            watcher.MovementThreshold = 25

            ' Le status du capteur à changé
            AddHandler watcher.StatusChanged, AddressOf OnStatusChanged
            ' La position a changée
            AddHandler watcher.PositionChanged, AddressOf OnPositionChanged

            ' Démarrage de l'acquisition des données du capteur
            watcher.TryStart(False, TimeSpan.FromSeconds(1))

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "AddHandlerWatcher Error: " & ex.ToString)
        End Try
    End Sub

    Private Sub OnStatusChanged(ByVal sender As Object, ByVal e As GeoPositionStatusChangedEventArgs)

        Trace.WriteLine(DateTime.Now & " " & "OnStatusChanged")

        Try
            ' Les données ne sont pas disponible ou erreur lors de la lecture du capteur
            If e.Status = GeoPositionStatus.Disabled Then
                LabelRealTimeStatus.Content = "RealTime Status: " & "No Data available!"
                _timerSensorErr.Start()
            ElseIf e.Status = GeoPositionStatus.NoData Then
                LabelRealTimeStatus.Content = "RealTime Status: " & "Sensor Error!"
                _timerSensorErr.Start()
            End If

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "OnStatusChanged Error: " & ex.ToString)
        End Try
    End Sub

    Private Shared Function ExternalIp() As String

        Try
            Trace.WriteLine(DateTime.Now & " " & "JnWhatIsMyExternalIp")

            ' On fait une recherche pour connaitre son IP
            Dim strUrl As String = My.Settings.URLip

            ' Force l'acceptation du certificat SSL
            ServicePointManager.ServerCertificateValidationCallback =
             New RemoteCertificateValidationCallback(Function() True)

            Dim request As WebRequest = WebRequest.Create(strUrl)
            Dim response As WebResponse = request.GetResponse()

            Dim reader As New StreamReader(response.GetResponseStream())
            Dim strMyIp As String = reader.ReadToEnd()

            response.Close()
            reader.Close()
            reader.Dispose()

            Return strMyIp

        Catch ex As WebException
            Return "Unavailable"
        End Try
    End Function

    Private Sub PowerModeChanged(ByVal sender As Object, ByVal e As EventArgs)

        Trace.WriteLine(DateTime.Now & " " & "PowerModeChanged")

        Try
            If PowerManager.IsBatteryPresent Then
                ' Le niveau de la batterie est bas
                If PowerManager.BatteryLifePercent < 20 Then
                    If Not _timerBattery.IsEnabled Then
                        _timerBattery.Start()
                    End If
                End If
            Else
                ' La batterie est ok
                If _timerBattery.IsEnabled Then
                    _timerBattery.Stop()
                    BouttonBattery.Source = New BitmapImage(New Uri("Resources\batteryhigh.gif", UriKind.Relative))
                End If
            End If

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "PowerModeChanged: " & ex.ToString)
        End Try
    End Sub

    Private Sub TimerWiFiDetection(sender As Object, e As EventArgs) Handles _timerWiFi.Tick

        Try
            ' On scanne les réseaux WiFi disponibles
            If WifiScan() Then
                ButtonWifi.Source = New BitmapImage(New Uri("Resources\wifiactive.gif", UriKind.Relative))
            Else
                ButtonWifi.Source = New BitmapImage(New Uri("Resources\wifiinactive.gif", UriKind.Relative))
            End If

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "TimerWiFiDetection: " & ex.ToString)
        End Try
    End Sub

    Private Sub TimerBatteryState(sender As Object, e As EventArgs) Handles _timerBattery.Tick

        Trace.WriteLine(DateTime.Now & " " & "TimerBatteryState")

        Try
            ' On fait clignoter si le niveau la batterie est basse ou critique
            If _batteryState = "high" Then
                BouttonBattery.Source = New BitmapImage(New Uri("Resources\batterylow.gif", UriKind.Relative))
                _batteryState = "low"
            Else
                BouttonBattery.Source = New BitmapImage(New Uri("Resources\batteryhigh.gif", UriKind.Relative))
                _batteryState = "high"
            End If
        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "TimerBatteryState: " & ex.ToString)
        End Try
    End Sub

    Private Sub StopAddHandlerWatcher()

        Trace.WriteLine(DateTime.Now & " " & "StopAddHandlerWatcher")

        Try
            ' On arrête l'acquisition des données du capteur et supprime les Handler
            _watcher.Stop()
            RemoveHandler _watcher.StatusChanged, AddressOf OnStatusChanged
            RemoveHandler _watcher.PositionChanged, AddressOf OnPositionChanged

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "StopAddHandlerWatcher Error: " & ex.ToString)
        End Try
    End Sub

    Private Shared Sub MemoryClean()

        Trace.WriteLine(DateTime.Now & " " & "MemoireClean")

        Try
            ' on prend la main du process en cours
            Dim pProcess As Process = Process.GetCurrentProcess()

            ' on vide le plus possible la mémoire inutilisée par le process
            Dim bRes As Boolean = EmptyWorkingSet(pProcess.Handle)

            If Not bRes Then

                ' EmptyWorkingSet a échoué...(dans certains cas cela peut échouer !)
            End If

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "MemoireClean Error: " & ex.ToString)
        End Try
    End Sub

    Private Sub MainWindowClosing(sender As Object, e As CancelEventArgs) Handles Me.Closing

        Trace.WriteLine(DateTime.Now & " " & "MainWindowClosing")

        Application.Current.Shutdown()

        If _geosenseSensor Then
            StopAddHandlerWatcher()
        End If

        ' On désenregistre et désactive application Recovery & Restart
        ApplicationRestartRecoveryManager.UnregisterApplicationRestart()
        ApplicationRestartRecoveryManager.UnregisterApplicationRecovery()

        With My.Settings
            .LastLatitude = Nothing
            .LastLongitude = Nothing
            .Save()
        End With

        Trace.WriteLine(DateTime.Now & " " & "On ferme Comport et on ferme l'application")
        CloseComport()
    End Sub

    Private Sub MainWindowLoaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded

        Try
            Trace.WriteLine(DateTime.Now & " " & "MainWindowLoaded")

            If My.Settings.UseGpsHarware Then
                LabelRealTimeStatus.Content = "RealTime Status: " & "Establishing GPS fix..."
            Else
                LabelRealTimeStatus.Content = "RealTime Status: " & "Initializing..."
            End If

            ' On regarde si on utilise la triangulation WIFI ou un GPS hardware.
            If My.Settings.UseGpsHarware = False Then

                Try
                    ' Liste des capteurs présents
                    For Each _mysensors In SensorManager.GetAllSensors()
                        ' Guid du capteur de géolocalisation
                        Dim id As New Guid("ed4ca589-327a-4ff9-a560-91da4b48275e")
                        ' C'est le bon TypeID
                        If _mysensors.TypeID = id AndAlso _mysensors.FriendlyName = "Geosense Sensor" Then

                            ' Le capteur est prêt
                            _geosenseSensor = True
                        End If
                    Next _mysensors

                    ' Détection Wifi disponible
                    _timerWiFi.Start()

                    If _geosenseSensor Then
                        AddHandlerWatcher()
                    Else
                        LabelRealTimeStatus.Content = "RealTime Status: " & "Geosense Sensor not found!"
                        Trace.WriteLine(DateTime.Now & " " & "Geosense Sensor not found")

                        SensorNotPresent = True

                        Dim newform As New Message
                        newform.Owner = Me
                        newform.ShowDialog()
                        Return
                    End If

                Catch ex As COMException
                    LabelRealTimeStatus.Content = "RealTime Status: " & "Geosense Sensor not found!"
                    Trace.WriteLine(DateTime.Now & " " & "Geosense Sensor not found: " & ex.ToString)
                End Try
            Else
                Try ' Overture du port GPS
                    Trace.WriteLine(DateTime.Now & " " & "On ouvre PortCom")

                    OpenComPort()
                    _timerWiFi.Start()
                    _timerGpsScan.Start()

                Catch ex As COMException
                    LabelRealTimeStatus.Content = "RealTime Status: " & "GPS ComPort Error!"
                    Trace.WriteLine(DateTime.Now & " " & "GPS ComPort Error: " & ex.ToString)
                End Try
            End If

            ' On lance GeoLockUpdate
            _timerGeolockUpdate.Start()

        Catch ex As Exception
            MessageBox.Show("MainWindowLoaded Error: " & ex.ToString)
            Trace.WriteLine(DateTime.Now & " " & "MainWindowLoaded Error: " & ex.ToString)
        End Try
    End Sub

    Private Sub GeoLockUpdate(sender As Object, e As EventArgs) Handles _timerGeolockUpdate.Tick

        ' On stoppe le timer
        _timerGeolockUpdate.Stop()

        ' On regarde au lancement de GeoLock si il y a une mise à jour Disponible
        If My.Settings.AutoUpdate Then
            XmlParser()

            If Settings.GeolockUpdate Then
                Dim newform As New Message
                newform.Owner = Me
                newform.ShowDialog()
            End If
        End If
    End Sub

    Private Sub ButtonPlusMouseDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs) _
     Handles ButtonPlus.MouseDown

        Trace.WriteLine(DateTime.Now & " " & "ButtonPlusMouseDown")

        Try
            If _bingMapLoaded Then
                If My.Settings.ZoomLevel < 18 Then

                    ' ZoomLevel plus
                    BingMap.ZoomLevel += 1
                    My.Settings.ZoomLevel += 1
                    My.Settings.Save()

                    MemoryClean()
                End If
            End If

            Trace.WriteLine(DateTime.Now & " " & "Le ZoomLevel est a" & " " & BingMap.ZoomLevel & "x")

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "BoutonPlusMouseDown Error: " & ex.ToString)
        End Try
    End Sub

    Private Sub ButtonMinusMouseDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs) _
     Handles ButtonMinus.MouseDown

        Trace.WriteLine(DateTime.Now & " " & "ButtonMinusMouseDown")

        Try
            If _bingMapLoaded Then
                If My.Settings.ZoomLevel > 13 Then

                    ' ZoomLevel minus
                    BingMap.ZoomLevel -= 1
                    My.Settings.ZoomLevel -= 1
                    My.Settings.Save()

                    MemoryClean()
                End If
            End If

            Trace.WriteLine(DateTime.Now & " " & "Le ZoomLevel est a" & " " & BingMap.ZoomLevel & "x")

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "BoutonMinusMouseDown Error: " & ex.ToString)
        End Try
    End Sub

    Private Sub ButtonRefreshMouseDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs) _
     Handles ButtonRefresh.MouseDown

        Trace.WriteLine(DateTime.Now & " " & "ButtonRefreshMouseDown")

        Try

            ' Déclarations du Custom PushPin
            Dim mapLayer As New MapLayer()
            Dim customPushPin As New Image()
            With customPushPin
                If My.Settings.UseGpsHarware Then
                    .Source = New BitmapImage(New Uri("Resources\CustomPushPin.png", UriKind.Relative))
                    .Width = 32
                    .Height = 32
                Else
                    .Source = New BitmapImage(New Uri("Resources\CustomPushPin.png", UriKind.Relative))
                    .Width = 32
                    .Height = 32
                End If
            End With

            ' Suppression at ajout du MapLayer
            With BingMap.Children
                .Clear()
                .Add(mapLayer)
            End With

            Dim location As New Location()
            With location
                .Latitude = My.Settings.LastLatitude
                .Longitude = My.Settings.LastLongitude
            End With

            ' Ajout du Custom PushPin sur le MapLayer
            mapLayer.SetPosition(customPushPin, location)
            mapLayer.SetPositionOrigin(customPushPin, PositionOrigin.Center)
            mapLayer.Children.Add(customPushPin)

            With BingMap
                ' C'est un refresh. On déplace la carte (pas besoin de request).
                .AnimationLevel = AnimationLevel.None
                .UseInertia = True
                .Center = location
                .ZoomLevel = My.Settings.ZoomLevel
                Trace.WriteLine(DateTime.Now & " " & "Déplacement de la carte après un refresh (pas de request)")
            End With

            MemoryClean()

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "ButtonRefreshMouseDown Error: " & ex.ToString)
        End Try
    End Sub

    Private Shared Function ConnexionInternetPresente() As Boolean

        ' On regarde si il y a une connexion Internet
        If IsNetConnectOnline() Then
            Return True
        Else
            Return False
        End If
    End Function

    Private Shared Function IsNetConnectOnline() As Boolean

        Trace.WriteLine(DateTime.Now & " " & "IsNetConnectOnline")

        ' Retourne True si il y a une connexion Internet
        IsNetConnectOnline = CBool(InternetGetConnectedState(0&, 0&))
    End Function

    Private Shared Function ConnectionAvailable(ByVal strServer As String) As Boolean

        ' Retourne True si le site ou serveur est disponible
        Try
            Dim reqFp As HttpWebRequest = CType(HttpWebRequest.Create(strServer), HttpWebRequest)
            reqFp.Timeout = 3000

            Dim rspFp As HttpWebResponse = CType(reqFp.GetResponse(), HttpWebResponse)

            If HttpStatusCode.OK = rspFp.StatusCode Then
                ' HTTP = 200 - Site ou serveur disponible
                reqFp.Abort()
                rspFp.Close()
                Return True
            Else
                ' Autres status - Site ou serveur indisponible
                reqFp.Abort()
                rspFp.Close()
                Return False
            End If

        Catch ex As WebException
            Trace.WriteLine(DateTime.Now & " " & "erreur try catch dans ConnectionAvailable")
            ' Site ou serveur indisponible
            Return False
        End Try
    End Function

    Private Sub ButtonInfoMouseDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs) _
     Handles ButtonInfo.MouseDown

        Trace.WriteLine(DateTime.Now & " " & "ButtonInfoMouseDown")

        Dim newform As New About
        newform.Owner = Me
        newform.ShowDialog()

        Top = FormTop
        Left = FormLeft
    End Sub

    Private Sub ButtonSettingsMouseDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs) _
     Handles ButtonSettings.MouseDown

        Trace.WriteLine(DateTime.Now & " " & "ButtonInfoMouseDown")

        Dim newform As New Settings
        newform.Owner = Me
        newform.ShowDialog()

        Top = FormTop
        Left = FormLeft
    End Sub

    Private Sub OpenComPort()

        Trace.WriteLine(DateTime.Now & " " & "OpenComport")

        Try

            If _spa IsNot Nothing Then
                If _spa.IsOpen Then
                    Trace.WriteLine(DateTime.Now & " " & "Le port est déjà ouvert")
                    Return
                End If
            End If

            _spa = New SerialPort("COM" & CType(My.Settings.ComPort, String), My.Settings.ComSpeed, Parity.None, 8, StopBits.One)
            _spa.Open()

        Catch ex As IOException
            Trace.WriteLine(DateTime.Now & " " & "Le port est déjà ouvert.")
        End Try
    End Sub

    Private Sub CloseComport()

        Trace.WriteLine(DateTime.Now & " " & "CloseComport")

        Try

            If _spa Is Nothing Then
                Return
            End If

            If _spa.IsOpen Then
                _spa.Close()
                _spa = Nothing
            Else
                Trace.WriteLine(DateTime.Now & " " & "Le port n'est pas ouvert.")
                Return
            End If

        Catch ex As IOException
            Trace.WriteLine(DateTime.Now & " " & "Le port n'est pas ouvert.")
        End Try
    End Sub

    Private Sub TimerGpsScanTick(sender As Object, e As EventArgs) Handles _timerGpsScan.Tick

        Try

            ' C'est le 1er démarrage. On recherche l'ip
            If _firstStart Then
                If _internetConnection Then
                    _ipAdd = ExternalIp()
                End If
            End If

            If _spa.IsOpen Then
                Dim data As String = _spa.ReadExisting()
                Dim strArr() As String = data.Split("$"c)
                For i As Integer = 0 To strArr.Length - 1
                    Dim strTemp As String = strArr(i)
                    Dim lineArr() As String = strTemp.Split(","c)
                    If lineArr(0) = "GPGGA" Then

                        ' Latitude
                        _latitude = ToDecimal(lineArr(2))

                        ' Longitude
                        _longitude = ToDecimal(lineArr(4))

                        ' Satellites In View
                        If lineArr(7) <> "" Then
                            LabelSatelliteInView.Content = "Satellites In View: " & lineArr(7)
                        Else
                            LabelSatelliteInView.Content = "Satellites In View: " & "Unknow"
                        End If

                        ' Dilution Of Precision
                        If lineArr(8) <> "" Then
                            _dilution = Replace(lineArr(8), ".", ",")
                            _dilutiondouble = CType(_dilution, Double)
                            LabelErrorRadiusMeters.Content = "Error Radius Meters: ± " & (_dilutiondouble * 10).ToString("0.0")
                        Else
                            LabelErrorRadiusMeters.Content = "Error Radius Meters: " & "Unknow"
                        End If

                        ' Altitude
                        If lineArr(9) <> "" Then
                            _altitude = lineArr(9)
                            _altitudedouble = Convert.ToDouble(_altitude)
                            LabelAltitudeSeaLevelMeters.Content = "Altitude SeaLevel Meters: " & _altitudedouble.ToString("0.0")
                        Else
                            LabelAltitudeSeaLevelMeters.Content = "Altitude SeaLevel Meters: " & "Unknow"
                        End If

                        LabelBearingDegrees.Content = "Bearing Degrees: " & "None"
                        LabelSpeedMetersSecond.Content = "Speed Meters Second: " & "None"
                    End If
                Next i

                If _
                    Math.Abs(_latitude - My.Settings.LastLatitude) > 0.000001 OrElse
                    Math.Abs(_longitude - My.Settings.LastLongitude) > 0.000001 Then

                    ' Ce n'est pas le 1er démarrage. On recherche l'ip
                    If (Not _firstStart) AndAlso ConnexionInternetPresente() Then
                        If _ipAdd <> "Unavailable" Then
                            _ipAdd = ExternalIp()
                        End If
                        _internetConnection = True
                    End If

                    LabelTimeStamp.Content = "TimeStamp: " & DateTime.Now
                    LabelLatitudeDegrees.Content = "Latitude Degrees: " & _latitude.ToString("0.000000")
                    LabelLongitudeDegrees.Content = "Latitude Degrees: " & _longitude.ToString("0.000000")
                    LabelAdress1.Content = "Adress1: " & "Unknow"
                    LabelAdress2.Content = "Adress2: " & "None"
                    LabelCity.Content = "City: " & "Unknow"
                    LabelCountryRegion.Content = "Country Region: " & "Unknow"
                    LabelStateProvince.Content = "State Province: " & "Unknow"
                    LabelPostalCode.Content = "Postal Code: " & "Unknow"
                    LabelRealTimeStatus.Content = "RealTime Status: Acquisition In Progress..."

                    ' Si il y a une connection internet on essaye le reverse geocoding
                    If ConnexionInternetPresente() AndAlso ConnectionAvailable("http://dev.virtualearth.net/") Then

                        Dim latitudeDecimal As String = CStr(_latitude.ToString("0.000000")).Replace(",", ".")
                        Dim longitudeDecimal As String = CStr(_longitude.ToString("0.000000")).Replace(",", ".")

                        Dim uri As String = _httpsReverseGeocoding & "dev.virtualearth.net/REST/v1/Locations/" &
                                            latitudeDecimal & "," &
                                            longitudeDecimal & "?o=xml&key=" & My.Settings.BingMapKey

                        ' Code équivalent de l'Application.DoEvents()
                        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, New ThreadStart(Sub()
                                                                                                             End Sub))

                        ' On parse les données XML
                        Dim reader As New XmlTextReader(uri)
                        Dim field As String = String.Empty

                        Do While reader.Read()
                            Select Case reader.NodeType
                                Case XmlNodeType.Element
                                    field = reader.Name
                                Case XmlNodeType.Text
                                    With field
                                        If .Equals("AddressLine") Then
                                            LabelAdress1.Content = "Adress1: " & reader.Value
                                        End If
                                        If .Equals("AdminDistrict") Then
                                            LabelStateProvince.Content = "State Province: " & reader.Value
                                        End If
                                        If .Equals("CountryRegion") Then
                                            LabelCountryRegion.Content = "Country Region: " & reader.Value
                                        End If
                                        If .Equals("Locality") Then
                                            LabelCity.Content = "City: " & reader.Value
                                        End If
                                        If .Equals("PostalCode") Then
                                            LabelPostalCode.Content = "Postal Code: " & reader.Value
                                            Exit Do
                                        End If
                                    End With
                            End Select
                        Loop
                        reader.Close()
                    End If

                    ' Affichage de l'IP
                    If _ipAdd IsNot String.Empty Then
                        LabelCurrentIP.Content = "Current IP: " & _ipAdd
                    Else
                        LabelCurrentIP.Content = "Current IP: Unresolved"
                    End If

                    Try
                        ' Il y a une connexion internet ou la Maps est déjà chargée en mémoire
                        If _internetConnection OrElse _bingMapLoaded Then

                            ' Afficher la maps et PushPin
                            If _latitude > 0.0 AndAlso _longitude > 0.0 Then

                                Dim location As New Location()
                                With location
                                    .Latitude = _latitude
                                    .Longitude = _longitude
                                End With

                                ' Déclarations du Custom PushPin
                                Dim mapLayer As New MapLayer()
                                Dim customPushPin As New Image()
                                With customPushPin
                                    .Source = New BitmapImage(New Uri("Resources\CustomPushPin.png", UriKind.Relative))
                                    .Width = 25
                                    .Height = 28
                                End With

                                ' Suppression et ajout du MapLayer
                                With BingMap.Children
                                    .Clear()
                                    .Add(mapLayer)
                                End With

                                ' Ajout du Custom PushPin sur le MapLayer
                                mapLayer.SetPosition(customPushPin, location)
                                mapLayer.SetPositionOrigin(customPushPin, PositionOrigin.Center)
                                mapLayer.Children.Add(customPushPin)

                                ' Affichage de la maps
                                With BingMap
                                    ' C'est le premier démarrage
                                    If _firstStart Then
                                        .Heading = _bearing
                                        .AnimationLevel = AnimationLevel.None
                                        .UseInertia = True
                                        .SetView(location, My.Settings.ZoomLevel)
                                        Trace.WriteLine(DateTime.Now & " " & "Premier démarrage affichage de la carte")
                                    Else
                                        ' On déplace la carte (pas besoin de request).
                                        .Heading = _bearing
                                        .AnimationLevel = AnimationLevel.None
                                        .UseInertia = True
                                        .Center = location
                                        .ZoomLevel = My.Settings.ZoomLevel
                                        Trace.WriteLine(DateTime.Now & " " & "Déplacement de la carte après changement de location (pas de request)")
                                    End If
                                End With

                                ' La Maps est chargée
                                If BingMap IsNot Nothing Then
                                    _bingMapLoaded = True
                                End If

                                ' On affiche la Maps
                                If BingMap.Visibility = Visibility.Hidden Then
                                    BingMap.Visibility = Visibility.Visible
                                End If

                                ' L'adresse est connue. On arrête les Timer
                                _timerSensorErr.Stop()

                                Trace.WriteLine(DateTime.Now & " " & _latitude & " " & _longitude)

                            End If
                        Else
                            ' Pas de connexion internet
                            _bingMapLoaded = False
                            BingMap.Visibility = Visibility.Hidden
                        End If

                        ' On sauvegarde la position actuelle
                        With My.Settings
                            .LastLatitude = _latitude
                            .LastLongitude = _longitude
                        End With

                        _internetConnection = False
                        _firstStart = False

                    Catch ex As Exception
                        ' La maps n'est pas disponible
                        _bingMapLoaded = False
                        BingMap.Visibility = Visibility.Hidden
                    End Try
                End If

                ' Nettoyage de la mémoire
                MemoryClean()

            End If

        Catch ex As Exception
            BingMap.Visibility = Visibility.Hidden
            Trace.WriteLine(DateTime.Now & " " & "GpsSuccessfulFix Error: " & ex.ToString)
        End Try
    End Sub

    Private Function ToDecimal(ByVal pos As String) As Double

        ' Convertir en décimal
        Dim posDb As Double = CType(Replace(pos, ".", ","), Double)
        Dim deg As Double = Math.Floor(posDb / 100)
        Dim decPos As Double = Math.Round(deg + ((posDb - (deg * 100)) / 60), 5)

        ' Si on se trouve dans l'hémisphère occidental (Amérique / Etats-Unis) ou de l'hémisphère sud (Australie) il faut convertir en valeur négative.
        If My.Settings.Hemisphere Then
            decPos = decPos * -1
        End If
        Return decPos
    End Function

    Private Sub MainWindowLocationChanged(sender As Object, e As EventArgs) Handles Me.LocationChanged

        If Not _firstStart Then
            MyWindowMove = True
        End If
    End Sub

    Private Function WifiScan() As Boolean

        Try
            Dim networkIndex As Integer = -1
            Dim networks As String(,) = New String(99, 8) {}

            Dim proc As Process
            proc = New Process()
            With proc.StartInfo
                .CreateNoWindow = True
                .FileName = "netsh"
                .Arguments = "wlan show networks mode=bssid"
                .RedirectStandardOutput = True
                .UseShellExecute = False
                proc.Start()
            End With

            Dim output As String = proc.StandardOutput.ReadToEnd()
            proc.WaitForExit()
            Dim sr As StringReader = New StringReader(output)
            Dim line As String

            line = sr.ReadLine()
            Do While line IsNot Nothing
                If line.StartsWith("General Failure") Then
                    ' Carte Wifi désactivée ou non installée
                    Exit Do
                End If
                If line.StartsWith("SSID") Then
                    networkIndex += 1
                    For i As Integer = 0 To 8
                        ' Prévient les exceptions lorsque le champs est vide durant la recherche
                        networks(networkIndex, i) = " "
                    Next i
                   
                    line = sr.ReadLine()
                    Continue Do
                End If

                line = sr.ReadLine()
            Loop
            If networkIndex >= 0 Then
                ' WiFi disponible
                Return True
            Else
                ' WiFi non disponible
                Return False
            End If

        Catch ex As Exception
            Trace.WriteLine(DateTime.Now & " " & "WifiScan: " & ex.ToString)
            Return False
        End Try
    End Function

    Public Shared Sub XmlParser()

        ' Force l'acceptation du certificat SSL
        ServicePointManager.ServerCertificateValidationCallback =
            New RemoteCertificateValidationCallback(Function() True)

        If ConnexionInternetPresente() AndAlso ConnectionAvailable("https://zguidetv.tuxfamily.org/geolock_version.xml") Then

            ' La variable newVersion contient la version du fichier xml 
            Dim newVersion As Version = Nothing
            Dim reader As XmlTextReader = Nothing

            Try
                ' Fourni l'url contenue dans le document xml
                Const xmlUrl As String = "https://zguidetv.tuxfamily.org/geolock_version.xml"
                reader = New XmlTextReader(xmlUrl)
                reader.MoveToContent()

                Dim elementName As String = Nothing
                ' On controle si le fichier xml contient le noeud "zguidetv"
                If (reader.NodeType = XmlNodeType.Element) AndAlso (reader.Name = "geolock") Then
                    Do While reader.Read()
                        ' Quand on trouve un noeud on se souvient de son nom  
                        If reader.NodeType = XmlNodeType.Element Then
                            elementName = reader.Name
                        Else
                            ' noeud suivant 
                            If (reader.NodeType = XmlNodeType.Text) AndAlso (reader.HasValue) Then
                                ' On controle le nom des noeuds  
                                Select Case elementName
                                    Case "version"
                                        ' On parse le num de version
                                        ' dans le format :  xxx.xxx.xxx.xxx
                                        newVersion = New Version(reader.Value)
                                End Select
                            End If
                        End If
                    Loop
                End If

            Catch ex As Exception
            Finally
                If reader IsNot Nothing Then
                    reader.Close()
                End If
            End Try

            ' On regarde le numéro de la version actuelle  
            Dim curVersion As Version = Assembly.GetExecutingAssembly().GetName().Version

            ' On compare les versions
            If curVersion.CompareTo(newVersion) < 0 Then
                Settings.GeolockUpdate = True
            Else
                Settings.GeolockUpdate = False
            End If

            reader.Close()
        Else
            Return
        End If
    End Sub
End Class
